/**
 * vk-uni-pay 统一支付
 * 请勿修改此处代码，因为插件更新后此处代码会被覆盖。
 * 作者：VK
 * 发布于：2021-07-06
 */
'use strict';

const crypto = require("crypto");

const configCenter = require("uni-config-center");

const config = configCenter({ pluginId: 'uni-pay' }).requireFile('config.js');

const dao = require('./dao');

const libs = require('./libs');

const openapi = require('./openapi');

const uniPay = libs.uniPay;

var vkPay = {};

vkPay.libs = libs;
vkPay.request = libs.common.request;
vkPay.crypto = libs.crypto;

const notifyPath = "/vk-pay-notify/";

const db = uniCloud.database();
const _ = db.command;

/**
 * 获取支付插件的配置（如果不传参数，则获取到的是完整配置）
 * 特别注意：请不要将密钥信息返回给前端，防止密钥泄露。
 * 特别注意：请不要将密钥信息返回给前端，防止密钥泄露。
 * 特别注意：请不要将密钥信息返回给前端，防止密钥泄露。
 * 只在云函数中调用和使用，且结果不return给前端，则是安全的。
 */
vkPay.getConfig = (key) => {
	if (key) {
		return libs.common.getData(config, key);
	} else {
		return config;
	}
}

/**
 * 支付成功 - 异步通知
 */
vkPay.paymentNotify = async (obj = {}) => {
	let { event, context, orderPaySuccess } = obj;
	// console.log('event: ', event);
	let notifyPathSplit = event.path.substring(notifyPath.length).split("/");
	let pay_type = notifyPathSplit[0]; // 获取支付方式
	let out_trade_no = notifyPathSplit[1]; // 获取订单号
	if (!out_trade_no) {
		let notifyData = libs.common.getNotifyData(event, pay_type);
		out_trade_no = notifyData.out_trade_no;
	}
	// 根据out_trade_no获取支付订单信息
	let payOrderInfo = await dao.payOrders.findByOutTradeNo(out_trade_no);
	if (!payOrderInfo) {
		console.log('---------!支付订单信息不存在!---------');
		console.log('---------!支付订单信息不存在!---------');
		console.log('---------!支付订单信息不存在!---------');
		return libs.common.returnNotifySUCCESS(pay_type);
	}
	if (!pay_type) pay_type = payOrderInfo.pay_type;
	if (payOrderInfo.pay_type !== pay_type) payOrderInfo.pay_type = pay_type;
	let uniPayInstance = await vkPay.initUniPayInstance(payOrderInfo);
	// uni-pay 1.0.17 新增了判断通知类型接口 checkNotifyType
	if (typeof uniPayInstance.checkNotifyType === "function") {
		let notifyType = await uniPayInstance.checkNotifyType(event);
		console.log("notifyType：", notifyType);
		if (notifyType !== "payment") {
			// 由于支付宝部分退款会触发支付成功的回调，但同时签名验证是算未通过的，为了避免支付宝重复推送，这里可以直接返回成功告知支付宝服务器，不用再推送过来了。
			return libs.common.returnNotifySUCCESS(pay_type);
		}
	}
	let verifyResult = await uniPayInstance.verifyPaymentNotify(event);
	if (!verifyResult) {
		console.log('---------!签名验证未通过!---------');
		console.log('---------!签名验证未通过!---------');
		console.log('---------!签名验证未通过!---------');
		return libs.common.returnNotifySUCCESS(pay_type);
	}
	console.log('---------!签名验证通过!---------');
	verifyResult = JSON.parse(JSON.stringify(verifyResult)); // 这一句代码有用，请勿删除。
	console.log('verifyResult: ', verifyResult);
	let {
		outTradeNo,
		totalFee,
		transactionId,
		tradeState, // 微信支付v3判断支付成功的字段
		resultCode, // 微信支付v2和支付宝支付判断成功的字段
		openid,
		appId,
		authAppId,
		sellerId,
	} = verifyResult;
	let wxpay_info;
	let alipay_info;
	let vkspay_info;
	let passback_params;
	if (pay_type.indexOf("wxpay") == 0) {
		// 微信支付
		wxpay_info = verifyResult;
		if (verifyResult.attach) {
			passback_params = decodeURIComponent(verifyResult.attach);
			try {
				passback_params = JSON.parse(passback_params);
			} catch (err) {}
		}
	} else if (pay_type.indexOf("alipay") == 0) {
		// 支付宝支付
		alipay_info = verifyResult;
		if (verifyResult.passbackParams) {
			passback_params = decodeURIComponent(verifyResult.passbackParams);
			try {
				passback_params = JSON.parse(passback_params);
			} catch (err) {}
		}
	} else if (pay_type.indexOf("vkspay") == 0) {
		// VksPay支付
		vkspay_info = verifyResult;
	}
	// 支付宝部分退款成功后，也会触发此回调，故需要判断此次回调是支付成功回调还是部分退款回调
	let isPayNotify = true;
	if (alipay_info && (alipay_info.outBizNo || alipay_info.refundFee)) {
		isPayNotify = false;
		console.log('---------!不是付款成功通知，已为您忽略!---------');
	}
	if ((resultCode == "SUCCESS" || tradeState === "SUCCESS") && isPayNotify) {
		let notify_date = Date.now();
		let newPayOrderInfo = await dao.payOrders.updateAndReturn({
			whereJson: {
				status: 0, // status:0 为必须条件，防止重复推送时的错误
				out_trade_no: outTradeNo, // 商户订单号
			},
			dataJson: {
				status: 1, // 设置为已付款
				transaction_id: transactionId, // 更新第三方支付单号
				pay_date: notify_date, // 更新付款时间（这里以通知时间作为付款时间）
				notify_date: notify_date, // 更新通知时间
				auth_appid: authAppId,
				seller_id: sellerId,
				pay_type, // 更新支付方式（重要）
				notify_num: _.inc(1), // 通知次数，这里只会是0或1
				openid,
				original_data: event, // 存本次的回调原始数据（为了留个备份，方便核查数据）
				wxpay_info: wxpay_info,
				alipay_info: alipay_info,
				vkspay_info: vkspay_info
			}
		});
		if (newPayOrderInfo) {
			payOrderInfo = newPayOrderInfo;
			// 只有首次推送才执行用户自己的逻辑处理。
			// 用户自己的逻辑处理 开始-----------------------------------------------------------
			let userOrderSuccess = false;
			if (typeof orderPaySuccess === "function") {
				console.log('用户自己的回调逻辑 - 开始执行');
				userOrderSuccess = await orderPaySuccess({
					verifyResult,
					data: payOrderInfo,
					passback_params,
				}, { db, _, ...obj });
				console.log('用户自己的回调逻辑 - 执行完成');
			}
			console.log('userOrderSuccess', userOrderSuccess);
			// 用户自己的逻辑处理 结束-----------------------------------------------------------
			try {
				await dao.payOrders.update({
					whereJson: {
						status: 1,
						out_trade_no: outTradeNo,
					},
					dataJson: {
						user_order_success: userOrderSuccess,
					}
				});
			} catch (err) {
				console.error("存本次的回调数据异常：", err);
			}
		} else {
			console.log('---------！注意：本次回调非首次回调，已被插件拦截，插件不会执行你的回调函数！---------');
			console.log('---------！注意：本次回调非首次回调，已被插件拦截，插件不会执行你的回调函数！---------');
			console.log('---------！注意：本次回调非首次回调，已被插件拦截，插件不会执行你的回调函数！---------');
			console.log('verifyResult:', verifyResult);
		}
	}
	// 给第三方服务器回复 开始-----------------------------------------------------------
	return libs.common.returnNotifySUCCESS(pay_type);
	// 给第三方服务器回复 结束-----------------------------------------------------------
};

/**
 * 统一支付 - 创建支付
 * @param {String} provider    供应商:wxpay、alipay
 * @param {Object} context     请求上下文
 * @param {Object} data        订单参数
 * data参数
 * @param {String} openid  		用户openid，小程序支付时必传
 * @param {String} out_trade_no  商户支付订单号
 * @param {Number} total_fee    订单金额(单位分 100 = 1元)
 * @param {String} subject     订单标题
 * @param {String} body        订单详情
 * @param {String} type        订单类型 goods：订单付款 recharge：余额充值付款 vip：vip充值付款 等等，可自定义
 * res 返回参数说明
 * @param {Number} code 错误码，0表示成功
 * @param {String} msg 详细信息
 * @param {String} out_trade_no 商户支付订单号
 * @param {Object} orderInfo  支付订单信息
 */
vkPay.createPayment = async (obj = {}) => {
	let {
		provider,
		context,
		alipayAppPayToH5Pay: myAlipayAppPayToH5Pay,
		needQRcode,
		isPC,
		data = {},
	} = obj;
	if (!context) context = uniCloud.$context;
	let {
		pid, // 商户id，若此参数有值，则会从数据库中获取支付配置进行支付
		app_auth_token, // 支付宝服务商模式下，子商户的授权token，若该参数有值，自动视为支付宝服务商模式
		sub_appid, // 微信支付服务商模式下，子商户的appid，若该参数有值，自动视为微信支付服务商模式
		sub_mchid, // 微信支付服务商模式下，子商户的mchid，与sub_appid搭配使用
		openid,
		out_trade_no,
		total_fee,
		subject,
		body,
		passback_params,
		type,
		user_id,
		nickname,
		return_url,
		time_expire, // 订单绝对超时时间（13位时间戳格式）
		profit_sharing,
		custom,
		other
	} = data;
	if (!out_trade_no || typeof out_trade_no !== "string") {
		return new libs.error({ code: 1001 });
	}
	if (!type || typeof type !== "string") {
		return new libs.error({ code: 1002 });
	}
	if (typeof total_fee !== "number" || total_fee <= 0 || total_fee % 1 !== 0) {
		return new libs.error({ code: 1003 });
	}
	if (!subject || typeof subject !== "string") {
		return new libs.error({ code: 1004 });
	}
	if (!provider || typeof provider !== "string") {
		return new libs.error({ code: 1005 });
	}
	if (provider === "wxpay" && !body) body = subject;
	let nowTime = Date.now();
	let res = { code: 0, msg: 'ok', out_trade_no };
	// 获取支付配置
	const {
		notifyUrl,
		sysServiceProviderId = "2088731216435275"
	} = config;
	let { alipayAppPayToH5Pay } = config;
	if (typeof myAlipayAppPayToH5Pay !== "undefined") alipayAppPayToH5Pay = myAlipayAppPayToH5Pay;
	// 业务逻辑开始-----------------------------------------------------------
	let spaceId = context.SPACEINFO.spaceId; // 服务空间ID
	let currentNotifyUrl = notifyUrl[spaceId]; // 异步回调地址
	// 兼容正式版阿里云与公测版阿里云spaceId开始-----------------------------------------------------------
	if (!currentNotifyUrl) {
		if (spaceId.indexOf("mp-") === 0) {
			currentNotifyUrl = notifyUrl[spaceId.substring(3)];
		} else {
			currentNotifyUrl = notifyUrl[`mp-${spaceId}`];
		}
	}
	// 兼容正式版阿里云与公测版阿里云spaceId结束-----------------------------------------------------------
	if (!currentNotifyUrl || currentNotifyUrl.indexOf("http") !== 0) {
		return new libs.error({ code: 101 });
	}
	let {
		PLATFORM,
		CLIENTUA = "",
		CLIENTIP,
		deviceId,
		appId: dcloudAppid
	} = context;

	PLATFORM = libs.common.getPlatform(PLATFORM);
	// 如果需要二维码支付模式，则强制定义以下参数
	if (needQRcode) {
		isPC = true;
		PLATFORM = "h5";
		openid = "";
		res.needQRcode = needQRcode;
	}
	if (PLATFORM === "h5" && provider === "alipay") {
		// 支付宝不需要openid
		openid = "";
	}
	if (PLATFORM === "h5" && provider === "wxpay" && libs.common.isMobile(CLIENTUA, isPC)) {
		if (CLIENTUA.toLowerCase().indexOf("micromessenger") == -1) {
			// 微信手机外部浏览器支付
			PLATFORM = "mweb";
		} else {
			PLATFORM = "h5-weixin";
			if (!openid) {
				return new libs.error({ code: 1006 });
			}
		}
	}
	if (provider === "appleiap") {
		PLATFORM = "app-plus";
	}
	let pay_type = provider + "_" + PLATFORM; // 支付方式
	if (pid) {
		alipayAppPayToH5Pay = await vkPay.getAlipayAppPayToH5Pay({ pid });
	}
	// 此处是为了让app支付直接调用H5支付,这样不用去申请APP申请接口权限
	if (alipayAppPayToH5Pay && pay_type == "alipay_app-plus") pay_type = "alipay_h5";
	// 拼接实际异步回调地址(在路径上体现了支付方式)
	let finalNotifyUrl = currentNotifyUrl + notifyPath + pay_type + "/" + out_trade_no;
	// 初始化uniPayInstance
	let tradeType = vkPay.getTradeType(pay_type);
	let uniPayConifg = await vkPay.getUniPayConfig({ pay_type, pid, app_auth_token, sub_appid, sub_mchid });
	let uniPayInstance = await vkPay.initUniPayInstance({ pay_type, pid, app_auth_token, sub_appid, sub_mchid });

	// 获取支付信息
	let getOrderInfoParam = {
		openid: openid,
		subject: subject,
		body: body,
		outTradeNo: out_trade_no,
		totalFee: total_fee,
		notifyUrl: finalNotifyUrl,
		tradeType: tradeType
	};
	if (pay_type === "wxpay_mweb") {
		getOrderInfoParam.spbillCreateIp = CLIENTIP;
		if (uniPayConifg.version === 3) {
			// v3版本特殊处理
			getOrderInfoParam.sceneInfo = JSON.parse(JSON.stringify(uniPayConifg.sceneInfo));
			if (getOrderInfoParam.sceneInfo.h5_info.wap_url) {
				getOrderInfoParam.sceneInfo.h5_info.app_url = getOrderInfoParam.sceneInfo.h5_info.wap_url;
				delete getOrderInfoParam.sceneInfo.h5_info.wap_url;
			}
			if (getOrderInfoParam.sceneInfo.h5_info.wap_name) {
				getOrderInfoParam.sceneInfo.h5_info.app_name = getOrderInfoParam.sceneInfo.h5_info.wap_name;
				delete getOrderInfoParam.sceneInfo.h5_info.wap_name;
			}
		} else {
			// v2版本
			getOrderInfoParam.sceneInfo = uniPayConifg.sceneInfo;
		}
	}
	// 仅vkspay支持returnUrl
	if (provider === "vkspay" && return_url) {
		getOrderInfoParam.returnUrl = return_url;
	}
	if (time_expire) {
		// 获取各支付供应商对应的timeExpire格式
		getOrderInfoParam.timeExpire = libs.common.getTimeExpire({ time_expire, provider, version: uniPayConifg.version });
	}
	res.pay_type = pay_type;
	res.provider = provider;
	res.total_fee = total_fee;
	res.platform = PLATFORM;
	// 第三方支付服务器返回的订单信息
	let orderInfo;
	try {
		// 如果是苹果内购，不需要执行uniPayInstance.getOrderInfo等操作
		if (provider !== "appleiap") {
			if (provider === "alipay") {
				// 支付宝支付特殊参数
				if (typeof data.extendParams === "undefined" && sysServiceProviderId) {
					getOrderInfoParam.extendParams = { sysServiceProviderId };
				}
				if (uniPayConifg.sysServiceProviderId) {
					getOrderInfoParam.extendParams = { sysServiceProviderId: uniPayConifg.sysServiceProviderId };
				}
				if (passback_params) {
					// 支付宝需要进行encodeURIComponent编码
					getOrderInfoParam.passbackParams = encodeURIComponent(JSON.stringify(passback_params));
				}
				// 判断是否需要分账（订单资金冻结）
				if (profit_sharing) {
					if (typeof data.extendParams === "undefined") data.extendParams = {};
					getOrderInfoParam.extendParams.royalty_freeze = true;
				}
			} else if (provider === "wxpay") {
				// 微信支付特殊参数
				if (passback_params) {
					getOrderInfoParam.attach = JSON.stringify(passback_params);
				}
				// 判断是否需要分账（订单资金冻结）
				if (profit_sharing) {
					if (uniPayConifg.version === 3) {
						getOrderInfoParam.settle_info = {
							profit_sharing: true
						};
					} else {
						getOrderInfoParam.profit_sharing = "Y";
					}
				}
			}
			if (other) {
				// other 内的键名转驼峰
				other = libs.common.snake2camelJson(other);
				getOrderInfoParam = Object.assign(getOrderInfoParam, other);
			}
			res.orderInfo = await uniPayInstance.getOrderInfo(getOrderInfoParam);
			// 支付宝支付参数特殊处理
			if (provider === "alipay") {
				if (typeof res.orderInfo === "object" && res.orderInfo.code && res.orderInfo.code !== "10000") {
					res.code = res.orderInfo.code;
					res.msg = res.orderInfo.subMsg;
				}
			}
			// 生成二维码图片base64
			if (needQRcode === "image" && res.orderInfo && res.orderInfo.codeUrl) {
				res.qrcodeImage = await libs.qrcode.toDataURL(res.orderInfo.codeUrl, {
					type: "image/png",
					width: 200,
					margin: 1,
					scale: 1,
					color: {
						dark: "#000000",
						light: "#ffffff",
					},
					errorCorrectionLevel: "Q",
					quality: 1
				});
			}
		}
	} catch (e) {
		let errMsg = e.errorMessage || e.message;
		console.error("getOrderInfoParam: ", getOrderInfoParam);
		console.error("data: ", data);
		console.error("err: ", e);
		console.error("errMsg: ", errMsg);
		return new libs.error({ code: 3001, subMsg: errMsg });
	}
	let transaction_id = res.orderInfo && res.orderInfo.transaction_id ? res.orderInfo.transaction_id : undefined;
	// 判断是否存在
	let payOrderInfo = await dao.payOrders.findByOutTradeNo(out_trade_no);
	if (!payOrderInfo) {
		// 添加数据库（数据库的out_trade_no字段需设置为唯一索引）
		let os = context.OS || context.os;
		let statPlatform = libs.common.getStatPlatform(PLATFORM, os);
		let uniPlatform = libs.common.getUniPlatform(PLATFORM);
		let extraData = {};
		if (transaction_id) {
			extraData = {
				transaction_id
			};
		}
		await dao.payOrders.add({
			status: 0,
			pid,
			app_auth_token,
			sub_appid,
			sub_mchid,
			appId: dcloudAppid,
			provider_appid: uniPayConifg.appId,
			pay_type,
			platform: PLATFORM,
			uni_platform: uniPlatform,
			client_ip: CLIENTIP,
			type,
			out_trade_no,
			...extraData,
			user_id,
			nickname, // 用户昵称冗余
			device_id: deviceId,
			openid,
			total_fee,
			description: subject,
			passback_params,
			create_date: nowTime,
			...custom,
			other,
			// 对接支付统计时需要
			stat_data: {
				platform: statPlatform,
				app_version: context.appVersion,
				app_version_code: context.appVersionCode,
				app_wgt_version: context.appWgtVersion,
				os: os,
				ua: context.ua,
				channel: context.channel ? context.channel : String(context.scene),
				scene: context.scene
			}
		});
	} else {
		// 如果订单已经存在，则只修改订单支付方式（用户可能先点微信支付，未付款，又点了支付宝支付）
		if (payOrderInfo.status !== 0) {
			let statusObj = {
				"-1": "已关闭",
				"1": "已支付",
				"2": "已支付",
				"3": "已退款"
			};
			let statusText = statusObj[String(payOrderInfo.status)] || "已支付或已关闭";
			return { code: -3, msg: `订单${statusText}，请勿重复支付` };
		}
		if (payOrderInfo.total_fee !== total_fee) {
			return { code: -1, msg: `订单金额不一致，请更换支付单号（out_trade_no）后重新发起` };
		}
		let extraData = {};
		if (transaction_id) {
			extraData = {
				transaction_id
			};
		}
		await dao.payOrders.updateById(payOrderInfo._id, {
			...extraData,
			pay_type,
			openid
		});
	}

	// 如果配置了自动删除过期未支付的订单，则执行下面的逻辑
	if (config.autoDeleteExpiredOrders > 0) {
		// 利用随机数达到大概每20次请求会执行1次删除未付款订单。
		let deleteRandom = Math.floor(Math.random() * 20 + 1);
		if (deleteRandom === 6) {
			// 自动删除N天前的订单（未付款订单）
			await dao.payOrders.deleteExpPayOrders({
				day: config.autoDeleteExpiredOrders
			});
		}
	}

	// 业务逻辑结束-----------------------------------------------------------
	return res;
};

/**
 * 统一支付结果查询
 * @description 根据商户订单号或者平台订单号查询订单信息，主要用于未接收到支付通知时可以使用此接口进行支付结果验证
 * data 请求参数 说明
 * @param {String} out_trade_no 商户订单号
 * res 返回参数说明
 * @param {Number} code 错误码，0表示成功
 * @param {String} msg 详细信息
 */
vkPay.queryPayment = async (obj = {}) => {
	let {
		out_trade_no, // 商户订单号
		transaction_id, // 支付平台的订单号
		await_notify = false, // 是否需要等待异步通知执行完成才返回前端支付结果
		await_max_time = 20, // 最大等待时长，默认20秒（单位秒）
		pay_order_info = false, // 是否需要返回订单信息
	} = obj;
	let res = { code: 0, msg: 'ok' };
	// 业务逻辑开始-----------------------------------------------------------
	if (!out_trade_no) {
		return new libs.error({ code: 1001 });
	}
	// 查询订单信息
	let payOrderInfo = await dao.payOrders.findByOutTradeNo(out_trade_no);
	if (!payOrderInfo) {
		return new libs.error({ code: 2001 });
	}
	// 初始化uniPayInstance
	let uniPayInstance = await vkPay.initUniPayInstance(payOrderInfo);
	let orderQueryJson = {};
	if (out_trade_no) {
		orderQueryJson.outTradeNo = out_trade_no;
	} else {
		orderQueryJson.transactionId = transaction_id;
	}
	// 查询是否已付款
	let queryResult;
	if (typeof uniPayInstance.orderQuery === "function") {
		queryResult = await uniPayInstance.orderQuery(orderQueryJson);
	} else {
		// 无uniPayInstance.orderQuery函数时的兼容处理
		if ([1, 2].indexOf(payOrderInfo.status) > -1) {
			queryResult = {
				tradeState: "SUCCESS",
				tradeStateDesc: "订单已支付"
			};
		} else if ([3].indexOf(payOrderInfo.status) > -1) {
			queryResult = {
				tradeState: "REFUNDED",
				tradeStateDesc: "订单已退款"
			};
		} else {
			queryResult = {
				tradeState: "NOPAY",
				tradeStateDesc: "订单未支付"
			};
		}
	}
	if (queryResult.tradeState === 'SUCCESS' || queryResult.tradeState === 'FINISHED') {
		// 如果已付款，再判断是否需要查询异步回调执行完毕
		if (typeof payOrderInfo.user_order_success == "undefined" && await_notify) {
			let whileTime = 0; // 当前循环已执行的时间（毫秒）
			let whileInterval = 500; // 每次循环间隔时间（毫秒）
			let maxTime = await_max_time * 1000; // 循环执行时间超过此值则退出循环（毫秒）
			while (typeof payOrderInfo.user_order_success == "undefined" && whileTime <= maxTime) {
				await libs.common.sleep(whileInterval);
				whileTime += whileInterval;
				payOrderInfo = await dao.payOrders.findByOutTradeNo(out_trade_no); // 查询最新的订单信息
			}
		}
		let notify_msg;
		if (typeof payOrderInfo.user_order_success === "undefined") {
			notify_msg = "回调进行中";
		} else if (payOrderInfo.user_order_success) {
			notify_msg = "回调成功";
		} else if (payOrderInfo.user_order_success) {
			notify_msg = "回调失败";
		}
		res = {
			code: 0,
			msg: "支付成功",
			notify_msg, // 回调是否成功的描述，仅描述用
			orderPaid: true, // 标记用户是否已付款成功（此参数只能表示用户确实付款了，但系统的异步回调逻辑可能还未执行完成）
			out_trade_no, // 商家订单号
			transaction_id, // 支付平台订单号
			status: payOrderInfo.status, // 标记当前支付订单状态 -1：已关闭 0：未支付 1：已支付 2：已部分退款 3：已全额退款
			user_order_success: payOrderInfo.user_order_success, // 用户异步通知逻辑是否全部执行完成，且无异常（建议前端通过此参数是否为true来判断是否支付成功）
			notify: payOrderInfo.user_order_success, // 兼容旧版 = user_order_success
		}
		if (pay_order_info) {
			res.payOrder = payOrderInfo;
		}
	} else {
		let errMsg = queryResult.tradeStateDesc || "未支付或已退款";
		if (errMsg.indexOf("订单发生过退款") > -1) {
			errMsg = "订单已退款";
		}
		res = {
			code: -1,
			msg: errMsg,
			orderPaid: false,
			out_trade_no, // 商家订单号
			transaction_id, // 支付平台订单号
		}
	}
	// 业务逻辑结束-----------------------------------------------------------
	return res;
};
/**
 * 查询退款结果
 * @description 提交退款申请后，通过调用该接口查询退款状态。
 * data 请求参数 说明
 * @param {String} out_trade_no 商户订单号
 * res 返回参数说明
 * @param {Number} code 错误码，0表示成功
 * @param {String} msg 详细信息
 */
vkPay.queryRefund = async (obj = {}) => {
	let {
		out_trade_no,
	} = obj;
	let res = { code: 0, msg: 'ok' };
	if (!out_trade_no) {
		return new libs.error({ code: 1001 });
	}
	let payOrderInfo = await dao.payOrders.findByOutTradeNo(out_trade_no);
	if (!payOrderInfo) {
		return new libs.error({ code: 2001 });
	}
	if (payOrderInfo.status <= 0) {
		return new libs.error({ code: 2002 });
	}
	if (payOrderInfo.status === 1 && !payOrderInfo.refund_num) {
		return new libs.error({ code: 2003 });
	}
	const pay_type = payOrderInfo.pay_type;
	let uniPayInstance = await vkPay.initUniPayInstance(payOrderInfo);
	let queryResult;
	try {
		let refundQueryJson = {
			outTradeNo: out_trade_no,
			outRefundNo: payOrderInfo.refund_list[0].out_refund_no,
		};
		queryResult = await uniPayInstance.refundQuery(refundQueryJson);
	} catch (err) {
		return {
			code: -1,
			msg: "查询失败，请稍后再试",
			err: err,
			queryResult: queryResult
		}
	}
	let orderInfo = {
		total_fee: payOrderInfo.total_fee,
		refund_fee: payOrderInfo.refund_fee,
		refund_num: payOrderInfo.refund_num,
		refund_list: payOrderInfo.refund_list,
		pay_type: payOrderInfo.pay_type,
		status: payOrderInfo.status,
		type: payOrderInfo.type,
		out_trade_no: payOrderInfo.out_trade_no,
		transaction_id: payOrderInfo.transaction_id,
	};
	if (queryResult.refundFee > 0) {
		let msg = "退款成功";
		if (payOrderInfo.refund_list && payOrderInfo.refund_list.length > 0) {
			msg = `合计退款 ${payOrderInfo.refund_fee/100}\r\n`;
			for (let i in payOrderInfo.refund_list) {
				let item = payOrderInfo.refund_list[i];
				let index = Number(i) + 1;
				let timeStr = libs.common.timeFormat(item.refund_date || item.refund_time, "yyyy-MM-dd hh:mm:ss");
				msg += `${index}、 ${timeStr} \r\n退款 ${item.refund_fee/100} \r\n`;
			}
		}
		res = {
			code: 0,
			msg: msg,
			orderInfo: orderInfo,
			queryResult: queryResult
		}
	} else {
		res = {
			code: -1,
			msg: "未退款",
			orderInfo: orderInfo,
			queryResult: queryResult
		}
	}

	return res;
};
/**
 * 统一退款
 * @description 当交易发生之后一段时间内，由于买家或者卖家的原因需要退款时，卖家可以通过退款接口将支付款退还给买家。
 * data 请求参数 说明
 * @param {String} out_trade_no 商户订单号
 * @param {String} out_refund_no 商户退款单号（不填会自动生成）
 * @param {String} refund_desc 退款原因
 * @param {Number} refund_fee 退款总金额
 * res 返回参数说明
 * @param {Number} code 错误码，0表示成功
 * @param {String} msg 详细信息
 */
vkPay.refund = async (obj = {}) => {
	let {
		out_trade_no,
		out_refund_no,
		refund_desc = "用户申请退款",
		refund_fee: myRefundFee,
		refund_fee_type = "CNY"
	} = obj;

	let res = { code: 0, msg: 'ok' };
	// 业务逻辑开始-----------------------------------------------------------
	if (!out_trade_no) {
		return new libs.error({ code: 1001 });
	}
	let payOrderInfo = await dao.payOrders.findByOutTradeNo(out_trade_no);
	if (!payOrderInfo) {
		return new libs.error({ code: 2001 });
	}
	if (payOrderInfo.status <= 0) {
		return new libs.error({ code: 2002 });
	}
	let refund_num = payOrderInfo.refund_num || 0;
	refund_num++;
	// 生成退款订单号
	let outRefundNo = `${out_trade_no}-${refund_num}`;
	if (out_refund_no) {
		outRefundNo = out_refund_no;
	}
	// 订单总金额
	let totalFee = payOrderInfo.total_fee;
	// 退款总金额
	let refundFee = myRefundFee || totalFee;
	const pay_type = payOrderInfo.pay_type;
	let uniPayInstance = await vkPay.initUniPayInstance(payOrderInfo);

	// 退款操作
	try {
		let refundData = {
			outTradeNo: out_trade_no,
			outRefundNo,
			totalFee,
			refundFee,
			refundDesc: refund_desc,
			refundFeeType: refund_fee_type
		};
		console.log(`执行退款请求开始`, refundData);
		console.log(`${out_trade_no} -- ${outRefundNo} -- ${totalFee/100} -- ${refundFee/100}`);

		res = await uniPayInstance.refund(refundData);

		console.log(`执行退款请求结束`, res);
	} catch (err) {
		console.error("执行退款请求异常", err);
		return { code: -1, msg: err.message, err }
	}
	if (res.refundFee) {
		res.code = 0;
		res.msg = "退款成功";
		// 修改数据库
		try {
			let time = Date.now();
			// 修改订单状态
			console.log("修改订单状态开始", payOrderInfo._id);
			payOrderInfo = await dao.payOrders.updateAndReturn({
				whereJson: {
					_id: payOrderInfo._id
				},
				dataJson: {
					status: 2,
					refund_fee: _.inc(refundFee),
					refund_num: refund_num,
					refund_date: time, // 更新最近一次退款时间
					// 记录每次的退款详情
					refund_list: _.unshift({
						refund_date: time,
						refund_fee: refundFee,
						out_refund_no: outRefundNo,
						refund_id: res.refundId,
						refund_desc
					})
				}
			});
			console.log("修改订单状态结束", payOrderInfo ? "修改成功" : "修改失败");
			if (payOrderInfo && payOrderInfo.refund_fee >= payOrderInfo.total_fee) {
				// 修改订单状态为已全额退款
				await dao.payOrders.updateById(payOrderInfo._id, {
					status: 3,
					refund_fee: payOrderInfo.total_fee,
				});
				console.log("订单已全额退款");
			}
		} catch (err) {
			console.error("修改订单状态异常", err);
		}
	} else {
		res.code = -1;
		res.msg = "退款失败";
		if (res.subMsg) res.msg = res.subMsg;
	}
	// 业务逻辑结束-----------------------------------------------------------
	return res;
};


/**
 * 关闭订单
 * @description 用于交易创建后，用户在一定时间内未进行支付，可调用该接口直接将未付款的交易进行关闭，避免重复支付。
 * data 请求参数 说明
 * @param {String} out_trade_no 商户单号
 * res 返回参数说明
 * @param {Number} code 错误码，0表示成功
 * @param {String} msg 详细信息
 */
vkPay.closeOrder = async (data = {}) => {
	let {
		out_trade_no, // 插件支付单号
	} = data;
	if (!out_trade_no) return new libs.error({ code: 1001 });
	let payOrderInfo = await dao.payOrders.findByOutTradeNo(out_trade_no);
	if (!payOrderInfo) return new libs.error({ code: 2001 });
	let { pay_type } = payOrderInfo;
	let uniPayInstance = await vkPay.initUniPayInstance(payOrderInfo);
	let result = await uniPayInstance.closeOrder({
		outTradeNo: out_trade_no
	});
	if ((pay_type.indexOf("wxpay") === 0 && result.resultCode === "SUCCESS") || (pay_type.indexOf("alipay") === 0 && result.code ===
			"10000")) {
		// 修改订单状态为已取消
		await dao.payOrders.update({
			whereJson: {
				_id: payOrderInfo._id,
				status: 0
			},
			dataJson: {
				status: -1,
				cancel_date: Date.now()
			}
		});
		return {
			code: 0,
			msg: "订单已关闭",
			result: result
		}
	} else {
		return {
			code: -1,
			msg: result.returnMsg || result.subMsg,
			result: result
		}
	}
};

/**
 * 转账到支付宝或微信零钱
 * @description 单笔转账接口是基于支付宝的资金处理能力
 * data 请求参数 说明
 * @param {String} account 收款人账号（支付宝专用）
 * @param {String} real_name 收款人姓名
 * @param {String} openid 当前用户的openid（微信专用）
 * @param {Number} amount 必填项，转账金额 单位分 100=1元
 * @param {String} out_biz_no 转账单号（需保证唯一）
 * @param {String} title 转账标题
 * @param {String} remark 转账备注
 * @param {String} pay_type 必填项，支付类型：wxpay（微信支付）、alipay（支付宝支付）
 * @param {Number} version 微信支付 支持 2和3
 * res 返回参数说明
 * @param {Number} code 错误码，0表示成功
 * @param {String} msg 详细信息
 */
vkPay.transfer = async (obj = {}) => {
	let { pay_type, version = 2 } = obj;
	if (pay_type === "alipay") {
		return vkPay.alipay.transfer(obj);
	} else if (pay_type === "wxpay") {
		if (version === 2) {
			return vkPay.wxpay.transferV2(obj);
		} else {
			return vkPay.wxpay.transferV3(obj);
		}
	} else {
		return { code: -1, msg: "不支持的支付方式" };
	}
};

/**
 * 获取对应支付配置
 * @param {String} pay_type 支付方式
 * @param {String} pid 商户id，若此参数有值，则会从数据库中获取支付配置进行支付
 * @param {String} app_auth_token 支付宝子商户授权token
 * @param {String} sub_appid 微信支付服务商模式下，子商户的appid，若该参数有值，自动视为微信支付服务商模式
 * @param {String} sub_mchid 微信支付服务商模式下，子商户的mchid，与sub_appid搭配使用
 * let uniPayConifg = await vkPay.getUniPayConfig({ pay_type, pid, app_auth_token, sub_appid, sub_mchid });
 */
vkPay.getUniPayConfig = async function(obj = {}) {
	let {
		pay_type,
		pid, // 商户id，若此参数有值，则会从数据库中获取支付配置进行支付
		app_auth_token,
		sub_appid,
		sub_mchid
	} = obj;
	let arr = pay_type.split("_");
	let type1 = arr[0];
	let type2 = arr[1];
	if (!pid && !config.alipay && !config.wxpay && !config.vkspay) {
		throw new Error(`uni-pay配置${type1}.${type2}节点配置错误`);
	}
	let payConfig;
	if (pid) {
		// 从数据库中获取支付配置
		payConfig = await dao.payConfig.findById(pid);
	} else {
		// 从配置文件中获取支付配置
		payConfig = config;
	}
	if (!payConfig || !payConfig[type1]) {
		throw new Error(`uni-pay配置${type1}.${type2}节点配置错误`);
	}
	let uniPayConfig;
	if (type2) {
		if (type2.indexOf("mp-") > -1) {
			uniPayConfig = payConfig[type1][type2] || payConfig[type1]["mp"]; // 兼容mp、mp-weixin、mp-alipay
		} else if (type2.indexOf("app-") > -1) {
			uniPayConfig = payConfig[type1][type2] || payConfig[type1]["app"]; // 兼容app、app-plus
		} else if (["h5", "web", "native"].indexOf(type2) > -1) {
			uniPayConfig = payConfig[type1]["h5"] || payConfig[type1]["native"]; // 兼容web、h5、native
		} else if (["h5-weixin"].indexOf(type2) > -1) {
			uniPayConfig = payConfig[type1][type2] || payConfig[type1]["jsapi"]; // 兼容h5-weixin、jsapi
		} else {
			uniPayConfig = payConfig[type1][type2];
		}
	}
	if (type1 === "vkspay") {
		uniPayConfig = payConfig[type1];
		if (!uniPayConfig.mchId || !uniPayConfig.key) {
			throw new Error(`uni-pay配置${type1}节点下的mchId和key不能为空`);
		}
	}
	if (!uniPayConfig) {
		throw new Error(`uni-pay配置${type1}.${type2}节点配置错误`);
	}
	if (!uniPayConfig.appId && !uniPayConfig.subMchId && ["appleiap", "vkspay"].indexOf(type1) === -1) {
		throw new Error(`uni-pay配置${type1}.${type2}节点下的appId不能为空`);
	}
	if (type1 === "alipay" && app_auth_token) {
		// 支付宝服务商模式需要额外增加 appAuthToken 参数
		uniPayConfig = Object.assign({
			appAuthToken: app_auth_token
		}, uniPayConfig);
	}
	if (type1 === "wxpay" && uniPayConfig.pfx) {
		// 微信支付证书 base64 转 buffer（数据库存的是 base64 格式的证书，而接口需要 buffer 格式的证书）
		uniPayConfig.pfx = Buffer.from(uniPayConfig.pfx, "base64");
	}
	if (type1 === "wxpay" && sub_mchid) {
		// 微信服务商模式需要额外增加 subAppId 和 subMchId 参数
		uniPayConfig = Object.assign({}, uniPayConfig, {
			subAppId: sub_appid,
			subMchId: sub_mchid,
		});
	}
	return uniPayConfig;
};
/**
 * 初始化uniPayInstance
 * @param {String} pay_type 支付方式
 * @param {String} app_auth_token 商户授权token
 * let uniPayInstance = await vkPay.initUniPayInstance({ pay_type, app_auth_token, pid });
 * let uniPayInstance = await vkPay.initUniPayInstance(orderInfo);
 */
vkPay.initUniPayInstance = async function(obj = {}) {
	let {
		pay_type
	} = obj;
	let uniPayConifg = await vkPay.getUniPayConfig(obj);
	let uniPayInstance;
	if (pay_type.indexOf("wxpay") == 0) {
		// 微信支付
		if (uniPayConifg.version === 3) {
			// 微信支付V3
			uniPayInstance = uniPay.initWeixinV3(uniPayConifg);
		} else {
			// 微信支付V2
			uniPayInstance = uniPay.initWeixin(uniPayConifg);
		}
	} else if (pay_type.indexOf("alipay") == 0) {
		// 支付宝
		uniPayInstance = uniPay.initAlipay(uniPayConifg);
	} else if (pay_type.indexOf("appleiap") == 0) {
		// ios内购
		uniPayInstance = uniPay.initAppleIapPayment(uniPayConifg);
	} else if (pay_type.indexOf("vkspay") == 0) {
		// VksPay支付
		uniPayInstance = new openapi.vkspay(uniPayConifg);
	} else {
		throw new Error(`msg:${pay_type} : 不支持的支付方式`);
	}
	return uniPayInstance;
};
// 获取交易方式
vkPay.getTradeType = function(pay_type) {
	let obj = {
		// 微信
		"wxpay_app-plus": "APP", // APP APP支付
		"wxpay_mp-weixin": "JSAPI", // JSAPI 小程序支付
		"wxpay_h5-weixin": "JSAPI", // JSAPI 公众号支付
		"wxpay_h5": "NATIVE", // NATIVE 网站二维码支付
		"wxpay_mweb": "MWEB", // MWEB 外部浏览器H5支付
		// 支付宝
		"alipay_app-plus": "APP", // APP APP支付
		"alipay_mp-alipay": "JSAPI", // JSAPI 支付宝小程序支付
		"alipay_h5": "NATIVE", // NATIVE 网站二维码支付、外部浏览器H5支付、APP支付也能用
	};
	return obj[pay_type];
};
// 获取alipayAppPayToH5Pay
// await vkPay.getAlipayAppPayToH5Pay({ pid });
vkPay.getAlipayAppPayToH5Pay = async function(obj = {}) {
	const {
		alipayAppPayToH5Pay
	} = config;
	let { pid } = obj;
	let result = alipayAppPayToH5Pay;
	if (pid) {
		// 如果数据库内配置了alipayAppPayToH5Pay，则以数据库内的为准
		let payConfig = await dao.payConfig.findById(pid);
		if (typeof payConfig.alipayAppPayToH5Pay === "boolean") {
			result = payConfig.alipayAppPayToH5Pay;
		}
	}
	return result;
};


/**
 * 初始化vkPayInstance
 * @param {String} provider [必填]支付类型：wxpay（微信支付）、alipay（支付宝支付）
 * @param {Number} version 接口版本，默认2 可填2或3（仅微信支付使用）
 * @param {String} platform [微信支付必填] 平台类型，如：h5、h5-weixin、mp-weixin、app-plus
 * @param {String} pid 商户id，若此参数有值，则会从数据库中获取支付配置
 * @param {String} app_auth_token [支付宝] 子商户授权token
 * @param {String} sub_appid 微信支付服务商模式下，子商户的appid，若该参数有值，自动视为微信支付服务商模式
 * @param {String} sub_mchid 微信支付服务商模式下，子商户的mchid，与sub_appid搭配使用
 * let vkPayInstance = await vkPay.initVkPayInstance(obj);
 */
vkPay.initVkPayInstance = async (obj = {}) => {
	let {
		provider,
		version,
		platform,
		pid,
		app_auth_token,
		sub_appid,
		sub_mchid,
	} = obj;

	if (platform == undefined) {
		throw new Error(`msg:platform不能为空`);
	}
	platform = libs.common.getPlatform(platform);
	// 获取支付配置
	let pay_type = `${provider}_${platform}`;
	let uniPayConifg = await vkPay.getUniPayConfig({ pay_type, pid, app_auth_token, sub_appid, sub_mchid });
	if (provider === "alipay") {
		// 支付宝
		return new openapi.alipay(uniPayConifg);
	} else if (provider === "wxpay") {
		if (!version) version = uniPayConifg.version || 2;
		if (version === 3) {
			// 微信支付v3版本
			return new openapi.wxpayV3(uniPayConifg);
		} else {
			// 微信支付v2版本
			return new openapi.wxpayV2(uniPayConifg);
		}
	} else {
		throw new Error(`msg:暂不支持${provider}`);
	}
};


////////////////////////////支付宝开始///////////////////////////////
vkPay.alipay = {};
/**
 * 转账到支付宝
 * data 请求参数
 * @param {String} account 收款人账号（支付宝专用）
 * @param {String} real_name 收款人姓名
 * @param {Number} amount 转账金额 单位分 100=1元
 * @param {String} out_biz_no 转账单号
 * @param {String} title 转账标题
 * @param {String} remark 转账备注
 * res 返回参数说明
 * @param {Number} code 错误码，0表示成功
 * @param {String} msg 详细信息
 */
vkPay.alipay.transfer = async (obj = {}) => {
	let {
		account,
		real_name,
		amount,
		out_biz_no,
		title = "转账",
		remark,
		check_name = true,
		app_auth_token,
		pid,
		platform = "transfer"
	} = obj;
	platform = libs.common.getPlatform(platform);
	// 参数验证开始-----------------------------------------------------------
	if (typeof amount !== "number") return new libs.error({ code: 1007 });
	if (amount <= 0) return new libs.error({ code: 1008 });
	if (!account) return new libs.error({ code: 1009 });
	if (check_name && !real_name) return new libs.error({ code: 1010 });
	// 参数验证结束-----------------------------------------------------------
	// 业务逻辑开始-----------------------------------------------------------
	let time = Date.now();
	if (!out_biz_no) out_biz_no = "ZZ" + libs.common.timeFormat(time, "yyyyMMddhhmmssS") + libs.common.random(13);
	// 金额要/100，因为单位是分
	amount = parseFloat((amount / 100).toFixed(2));
	// 获取支付配置
	let uniPayConifg = await vkPay.getUniPayConfig({ pay_type: "alipay_" + platform, pid, app_auth_token });
	// 请求参数
	let biz_content = {
		out_biz_no: out_biz_no,
		trans_amount: amount,
		order_title: title,
		product_code: "TRANS_ACCOUNT_NO_PWD",
		biz_scene: "DIRECT_TRANSFER",
		payee_info: {
			identity_type: check_name ? "ALIPAY_LOGON_ID" : "ALIPAY_USER_ID",
			identity: account,
			name: real_name,
		},
		remark
	}
	// 实例化
	let alipay = new openapi.alipay(uniPayConifg);
	let res = await alipay.transfer({
		biz_content,
		app_auth_token
	});
	if (res.code === 0) {
		res.msg = "转账成功";
	}
	// 业务逻辑结束-----------------------------------------------------------
	return res;
};

////////////////////////////支付宝结束///////////////////////////////
////////////////////////////微信支付开始///////////////////////////////

vkPay.wxpay = {};
/**
 * 转账到微信零钱
 * data 请求参数
 * @param {String} openid 当前用户的openid
 * @param {String} real_name 收款人姓名
 * @param {Number} amount 转账金额 单位分 100=1元
 * @param {String} out_biz_no 转账单号
 * @param {String} title 转账标题
 * @param {String} remark 转账备注
 * res 返回参数说明
 * @param {Number} code 错误码，0表示成功
 * @param {String} msg 详细信息
 */
vkPay.wxpay.transferV2 = async (obj = {}) => {
	let {
		openid,
		real_name,
		amount,
		out_biz_no,
		title = "转账",
		remark = "转账",
		platform,
		check_name = true,
		pid
	} = obj;
	if (typeof amount !== "number" || amount <= 0) {
		return new libs.error({ code: 1008 });
	}
	if (platform == undefined) {
		return new libs.error({ code: 1011 });
	}
	platform = libs.common.getPlatform(platform);
	if (!openid) return new libs.error({ code: 1012 });
	if (check_name && !real_name) return new libs.error({ code: 1010 });
	if (!out_biz_no) out_biz_no = "ZZ" + libs.common.timeFormat(new Date(), "yyyyMMddhhmmssS") + libs.common.random(13);
	// 获取支付配置
	let uniPayConifg = await vkPay.getUniPayConfig({ pay_type: "wxpay_" + platform, pid });
	// 业务逻辑开始-----------------------------------------------------------

	// 实例化
	let wxPayV2 = new openapi.wxpayV2(uniPayConifg);
	let res = await wxPayV2.transfer({
		partner_trade_no: out_biz_no,
		openid,
		check_name: check_name ? "FORCE_CHECK" : "NO_CHECK",
		re_user_name: real_name,
		amount,
		desc: remark
	});
	// 业务逻辑结束-----------------------------------------------------------
	return res;
};

/**
 * 转账到微信零钱（v3版本）支持批量转账，批量转账请看vk-pay官方文档
 * data 请求参数
 * @param {String} platform 若传了 platform，则appid = 配置中platform对应的appid
 * @param {String} appid 直接指定appid，若platform和appid均为空，则从配置中读取默认的appid
 * @param {String} openid 当前用户的openid
 * @param {String} real_name 收款人姓名
 * @param {Number} amount 转账金额 单位分 100=1元
 * @param {String} out_biz_no 转账单号
 * @param {String} title 转账标题
 * @param {String} remark 转账备注
 * res 返回参数说明
 * @param {Number} code 错误码，0表示成功
 * @param {String} msg 详细信息
 */
vkPay.wxpay.transferV3 = async (obj = {}) => {
	let {
		platform: openidPlatform,
		appid,
		openid,
		real_name,
		amount,
		out_biz_no,
		title = "转账",
		remark = "转账",
		pid,
		transfer_detail_list
	} = obj;
	let platform = "transfer";

	if (platform == undefined) {
		return new libs.error({ code: 1011 });
	}
	platform = libs.common.getPlatform(platform);
	if (!out_biz_no) out_biz_no = "ZZ" + libs.common.timeFormat(new Date(), "yyyyMMddhhmmssS") + libs.common.random(13);
	// 获取支付配置
	let uniPayConifg = await vkPay.getUniPayConfig({ pay_type: "wxpay_" + platform, pid });

	let requestData;
	if (transfer_detail_list) {
		// 批量转账
		requestData = {
			out_batch_no: out_biz_no,
			batch_name: obj.batch_name,
			batch_remark: obj.batch_remark,
			total_amount: obj.total_amount,
			total_num: transfer_detail_list.length,
			transfer_detail_list,
		};
	} else {
		// 单笔转账
		if (typeof amount !== "number" || amount <= 0) {
			return new libs.error({ code: 1008 });
		}
		if (!openid) return new libs.error({ code: 1012 });
		transfer_detail_list = [{
			out_detail_no: out_biz_no,
			transfer_amount: amount,
			transfer_remark: remark,
			openid,
			user_name: real_name
		}];
		requestData = {
			out_batch_no: out_biz_no,
			batch_name: title,
			batch_remark: remark,
			total_amount: amount,
			total_num: transfer_detail_list.length,
			transfer_detail_list,
		};
	}
	let realityUniPayConifg = Object.assign({}, uniPayConifg);
	if (appid) {
		realityUniPayConifg.appId = appid;
	} else if (openidPlatform) {
		let openidPlatformUniPayConifg = await vkPay.getUniPayConfig({ pay_type: "wxpay_" + openidPlatform, pid });
		realityUniPayConifg.appId = openidPlatformUniPayConifg.appId;
	}
	// 实例化
	let wxPayV3 = new openapi.wxpayV3(realityUniPayConifg);
	let res = await wxPayV3.transfer(requestData);
	if (res.code === 0) {
		res.msg = "转账申请已提交，请等待商家审核！";
	}
	// 业务逻辑结束-----------------------------------------------------------
	return res;
};

/**
 * 获取微信支付平台证书v3版本
 */
vkPay.wxpay.fetchCertificates = async (obj = {}) => {
	let res = { code: 0, msg: 'ok' };
	let {
		pid,
		platform = "transfer"
	} = obj;

	if (platform == undefined) {
		return new libs.error({ code: 1011 });
	}
	platform = libs.common.getPlatform(platform);
	// 获取支付配置
	let uniPayConifg = await vkPay.getUniPayConfig({ pay_type: "wxpay_" + platform, pid });

	// 实例化
	let wxPayV3 = new openapi.wxpayV3(uniPayConifg);
	res = await wxPayV3.fetchCertificates();
	// 业务逻辑结束-----------------------------------------------------------
	return res;
};

////////////////////////////微信支付结束///////////////////////////////
/**
 * code换取openid(微信公众号)
 */
vkPay.code2SessionWeixinH5 = async (data = {}) => {
	let { code, pid } = data;
	if (!code) return new libs.error({ code: 1013 });
	// 获取公众号支付的配置信息
	let pay_type = "wxpay_h5-weixin";
	let config = await vkPay.getUniPayConfig({ pay_type, pid });
	let appId = config.subAppId || config.appId;
	let secret = config.subSecret || config.secret;
	if (!appId) return { code: -1, msg: `uni-pay配置${pay_type}的appId不能为空` };
	if (!secret) return { code: -1, msg: `uni-pay配置${pay_type}的secret不能为空` };
	let requestRes = await libs.common.request({
		url: `https://api.weixin.qq.com/sns/oauth2/access_token?appid=${appId}&secret=${secret}&code=${code}&grant_type=authorization_code`
	});
	if (requestRes.errcode) {
		let msg = requestRes.errmsg;
		if (requestRes.errcode === 40163) msg = "该code已被使用，请重新获取";
		return {
			...requestRes,
			code: requestRes.errcode,
			msg
		};
	}
	// 转驼峰
	requestRes = libs.common.snake2camelJson(requestRes);
	return {
		...requestRes,
		code: 0,
		msg: "ok"
	};
};

/**
 * code换取openid(微信小程序)
 */
vkPay.code2SessionWeixinMP = async (data = {}) => {
	let { code, pid } = data;
	if (!code) return new libs.error({ code: 1013 });
	// 获取微信小程序支付的配置信息
	let pay_type = "wxpay_mp-weixin";
	let config = {};
	try {
		config = await vkPay.getUniPayConfig({ pay_type, pid });
	} catch (err) {
		config = {};
	}
	let appId = config.subAppId || config.appId;
	let secret = config.subSecret || config.secret;
	if (!appId || !secret) {
		// 如果配置了VksPay支付，则不报这个错误，因为VksPay支付不需要获取openid
		let vkspayConfig;
		try {
			vkspayConfig = await vkPay.getUniPayConfig({ pay_type: "vkspay", pid });
		} catch (err) {
			vkspayConfig = {};
		}
		if (vkspayConfig && vkspayConfig.mchId) {
			return { code: 0 };
		} else {
			if (!appId) return { code: -1, msg: `uni-pay配置${pay_type}的appId不能为空` };
			if (!secret) return { code: -1, msg: `uni-pay配置${pay_type}的secret不能为空` };
		}
	}
	let requestRes = await libs.common.request({
		url: `https://api.weixin.qq.com/sns/jscode2session?appid=${appId}&secret=${secret}&js_code=${code}&grant_type=authorization_code`
	});
	if (requestRes.errcode) {
		let msg = requestRes.errmsg;
		if (requestRes.errcode === 40163) msg = "该code已被使用，请重新获取";
		return {
			...requestRes,
			code: requestRes.errcode,
			msg
		};
	}
	// 转驼峰
	requestRes = libs.common.snake2camelJson(requestRes);
	return {
		...requestRes,
		code: 0,
		msg: "ok"
	};
};


/**
 * code换取openid(支付宝小程序)
 */
vkPay.code2SessionAlipay = async (data = {}) => {
	let { code, app_auth_token, pid } = data;
	if (!code) return new libs.error({ code: 1013 });
	// 获取支付宝小程序支付的配置信息
	let pay_type = "alipay_mp-alipay";
	let config = {};
	try {
		config = await vkPay.getUniPayConfig({ pay_type, pid, app_auth_token });
	} catch (err) {
		config = {};
	}
	let appId = config.appId;
	let privateKey = config.privateKey;
	// 如果配置了VksPay支付，则不报这个错误，因为VksPay支付不需要获取openid
	if (!appId || !privateKey) {
		let vkspayConfig;
		try {
			vkspayConfig = await vkPay.getUniPayConfig({ pay_type: "vkspay", pid });
		} catch (err) {
			vkspayConfig = {};
		}
		if (vkspayConfig && vkspayConfig.mchId) {
			return { code: 0 };
		} else {
			if (!appId) return { code: -1, msg: `uni-pay配置${pay_type}的appId不能为空` };
			if (!privateKey) return { code: -1, msg: `uni-pay配置${pay_type}的privateKey不能为空` };
		}
	}
	// 实例化
	let alipay = new openapi.alipay(config);
	let res = await alipay.code2SessionAlipay({ code });
	if (res.code !== 0) {
		return res;
	}
	return {
		code: 0,
		msg: 'ok',
		openid: res.result.user_id,
		user_id: res.result.user_id,
	}
};

vkPay.code2SessionWeixinAlipay = vkPay.code2SessionAlipay; // 兼容旧版

/**
 * 解析token并返回user_id，解析失败不会报错，会返回 undefined
 */
vkPay.checkTokenReturnUserId = async (obj = {}) => {
	let { uniIdToken, context } = obj;
	let user_id;
	if (!uniIdToken) {
		return undefined;
	}
	try {
		// 只是尝试解析token，如果解析失败，则直接返回 undefined
		let uniId = libs.uniIdToken.createInstance({ context });
		let tokenRes = await uniId.checkToken(uniIdToken, { autoRefresh: false });
		if (tokenRes.uid) user_id = tokenRes.uid;
	} catch (err) {
		return undefined;
	}
	return user_id;
}

/**
 * 根据user_id获取nickname
 */
vkPay.getUserNickname = async (user_id) => {
	let nickname;
	if (!user_id) return nickname;
	let userInfo = await dao.uniIdUsers.findById(user_id);
	if (userInfo && userInfo.nickname) {
		nickname = userInfo.nickname;
	}
	return nickname;
}

// ios内购相关
vkPay.appleiap = {};
/**
 * 验证ios凭据（ios没有异步回调，需要主动回调）
 */
vkPay.appleiap.verifyReceipt = async (obj = {}) => {
	let {
		out_trade_no,
		transaction_receipt,
		transaction_identifier,
		pid,
		pay_type,
	} = obj;
	if (!out_trade_no || typeof out_trade_no !== "string") {
		return new libs.error({ code: 1001 });
	}
	if (!transaction_receipt || typeof transaction_receipt !== "string") {
		return new libs.error({ code: 1014 });
	}
	if (!transaction_identifier || typeof transaction_identifier !== "string") {
		return new libs.error({ code: 1015 });
	}
	if (!pay_type) pay_type = `appleiap_app-plus`;
	// 初始化uniPayInstance
	let uniPayConifg = await vkPay.getUniPayConfig({ pay_type, pid });
	let uniPayInstance = await vkPay.initUniPayInstance({ pay_type, pid });
	let verifyReceiptRes = await uniPayInstance.verifyReceipt({
		receiptData: transaction_receipt
	});
	let userOrderSuccess = false;
	if (verifyReceiptRes.tradeState !== "SUCCESS") {
		return new libs.error({ code: 2002 });
	}
	// 支付成功
	let pay_date = Number(verifyReceiptRes.receipt.receipt_creation_date_ms); // 获取支付时间
	let inAppList = verifyReceiptRes.receipt.in_app || [];
	let inApp = inAppList.find((item) => {
		return item.transaction_id === transaction_identifier;
	});
	if (!inApp) {
		// 校验不通过
		return new libs.error({ code: 1016 });
	}

	let {
		quantity, // 购买数量
		product_id, // 对应的内购产品id
		transaction_id, // 第三方交易单号
	} = inApp;

	let receiptExpiresIn = uniPayConifg.receiptExpiresIn || 86400;
	if ((Date.now() - receiptExpiresIn * 1000) > pay_date) {
		return { code: 0, msg: "凭据已失效" };
	}
	// 查询该transaction_id是否使用过，如果已使用，则不做处理，通知前端直接关闭订单。
	let queryInfo = await dao.payOrders.find({
		transaction_id,
	});
	if (queryInfo) return { code: 0, msg: "凭据已被使用过" };
	// 否则，执行用户回调
	// 用户自己的逻辑处理 开始-----------------------------------------------------------
	let orderPaySuccess;
	let payOrderInfo = await dao.payOrders.findByOutTradeNo(out_trade_no);
	if (!payOrderInfo) {
		return new libs.error({ code: 2001 });
	}
	try {
		// 加载自定义异步回调函数
		orderPaySuccess = require(`../../service/pay-notify/${payOrderInfo.type}`);
	} catch (err) {
		try {
			// 加载自定义异步回调函数
			orderPaySuccess = require(`../../vk-pay/service/pay-notify/${payOrderInfo.type}`);
		} catch (err2) {
			console.log(err);
		}
	}
	if (typeof orderPaySuccess === "function") {
		payOrderInfo = await dao.payOrders.updateAndReturn({
			whereJson: {
				status: 0, // status:0 为必须条件，防止重复推送时的错误
				out_trade_no: out_trade_no, // 商户订单号
			},
			dataJson: {
				status: 1, // 设置为已付款
				transaction_id: transaction_id, // 第三方支付单号
				pay_date: pay_date,
				notify_date: pay_date,
				original_data: verifyReceiptRes
			}
		});
		if (!payOrderInfo) {
			return new libs.error({ code: 2001 });
		}
		console.log('用户自己的回调逻辑 - 开始执行');
		userOrderSuccess = await orderPaySuccess({
			verifyResult: verifyReceiptRes,
			data: payOrderInfo,
		}, { db, _, ...obj });
		console.log('用户自己的回调逻辑 - 执行完成');
		await dao.payOrders.updateAndReturn({
			whereJson: {
				status: 1,
				out_trade_no,
			},
			dataJson: {
				user_order_success: userOrderSuccess,
			}
		});
	}
	payOrderInfo = await dao.payOrders.findByOutTradeNo(out_trade_no);
	console.log('userOrderSuccess', userOrderSuccess);
	// 用户自己的逻辑处理 结束-----------------------------------------------------------
	return {
		code: 0,
		msg: "支付成功",
		orderPaid: true, // 标记用户是否已付款成功（此参数只能表示用户确实付款了，但系统的异步回调逻辑可能还未执行完成）
		out_trade_no, // 商家订单号
		transaction_id, // 支付平台订单号
		status: payOrderInfo.status, // 标记当前支付订单状态 -1：已关闭 0：未支付 1：已支付 2：已部分退款 3：已全额退款
		user_order_success: payOrderInfo.user_order_success, // 用户异步通知逻辑是否全部执行完成，且无异常（建议前端通过此参数是否为true来判断是否支付成功）
		notify: payOrderInfo.user_order_success, // 兼容旧版 = user_order_success
		payOrder: payOrderInfo
	};
}

module.exports = vkPay;
